package com.gofaraway.service.面试真题;

import static com.gofaraway.service.面试真题.LocalCacheUtil.getInfoByCacheOrService;

import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.Data;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @author ChenPeng
 * @Date 2025/2/18 21:19
 */
public class MeiTuan {

    @Autowired
    private AService aService;
    private final ConcurrentHashMap<Integer, ShopInfo> CACHE_MAP = new ConcurrentHashMap<>(20);


    /**
     * 题目1：使用本地缓存改写下面这个方法，要求：优先从本地缓存获取数据，如果本地缓存没有数据，再从AService获取数据，合并本地缓存中的数据和AService中的数据，返回给调用方。
     */
    public List<ShopInfo> getShopInfo(List<Integer> shopIds) {

        List<ShopInfo> result = Lists.newArrayList();
        shopIds.forEach(s -> {
            ShopInfo shopInfo = CACHE_MAP.get(s);
            if (Objects.nonNull(shopInfo)) {
                result.add(shopInfo);
            }
        });

        List<Integer> exIds = result.stream().map(ShopInfo::getId).collect(Collectors.toList());

        List<Integer> fromService = shopIds.stream().filter(s -> !exIds.contains(s)).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(fromService)) {
            Map<Integer, ShopInfo> shopInfo = aService.getShopInfo(shopIds);

            if (MapUtils.isNotEmpty(shopInfo)) {
                CACHE_MAP.putAll(shopInfo);
            }

            result.addAll(shopInfo.values().stream().collect(Collectors.toList()));
        }

        return result;
    }

    public static final ConcurrentHashMap<Long, ProductInfo> product_cache_map = new ConcurrentHashMap<>(20);

    /**
     * 题目2：写一个工具类，能满足优先从本地缓存获取数据，再调用服务，最后合并数据返回。使得getShopInfo和getProductInfo都能使用该工具类
     */
    public List<ProductInfo> getProductInfo(List<Long> productIds) {

        return getInfoByCacheOrService(productIds, product_cache_map, longs -> aService.getProductInfo(longs),
                ProductInfo::getId);
    }

}

class LocalCacheUtil {

    public static <K, V> List<V> getInfoByCacheOrService(List<K> keys, ConcurrentHashMap<K, V> cacheMap,
            Function<List<K>, Map<K, V>> serviceFn, Function<V, K> keyExtractor) {

        List<V> result = new ArrayList<>();

        // 1. 从缓存中获取数据
        List<V> fromCache = getInfoCache(keys, cacheMap);
        result.addAll(fromCache);

        // 2. 找出未命中缓存的键
        List<K> fromCacheKeys = fromCache.stream().map(keyExtractor).collect(Collectors.toList());

        List<K> fromServiceKeys = keys.stream().filter(k -> !fromCacheKeys.contains(k)).collect(Collectors.toList());

        // 3. 如果存在未命中缓存的键，则调用服务获取数据
        if (CollectionUtils.isNotEmpty(fromServiceKeys)) {
            Map<K, V> fromService = serviceFn.apply(fromServiceKeys);

            if (MapUtils.isNotEmpty(fromService)) {
                // 4. 将服务返回的数据放入缓存
                putInfoCache(fromService, cacheMap);
                result.addAll(fromService.values());
            }
        }

        return result;
    }

    public static <K, V> List<V> getInfoCache(List<K> keys, ConcurrentHashMap<K, V> map) {

        List<V> res = new ArrayList<>();
        keys.forEach(k -> {
            V v = map.get(k);
            if (v != null) {
                res.add(v);
            }
        });
        return res;
    }

    public static <K, V> void putInfoCache(Map<K, V> map, ConcurrentHashMap<K, V> cacheMap) {
        cacheMap.putAll(map);
    }

}

@Data
class ProductInfo {

    private Long id;
}

@Data
class ShopInfo {

    private Integer id;
}

class AService {

    Map<Integer, ShopInfo> getShopInfo(List<Integer> ids) {
        return null;
    }

    Map<Long, ProductInfo> getProductInfo(List<Long> ids) {
        return null;
    }
}

